`timescale 1ns / 1ps

// FIFO
module zq_fifo
#(
    parameter WIDTH = 128,
    parameter DEPTH = 192,
    parameter RAMTYPE = "block",       // auto, block, distributed, register
    parameter RAMLATENCY = 2
)
(
    input   clk,
    input   rst,

    input   in_vld,
    output  in_rdy,
    input   [WIDTH-1: 0]    din,

    output  out_vld,
    input   out_rdy,
    output  [WIDTH-1: 0]    dout
);

localparam ADDR_WIDTH = $clog2(DEPTH);

wire [ADDR_WIDTH-1: 0] addr_w;
wire last_w;
wire [ADDR_WIDTH-1: 0] addr_r;
wire last_r;
wire read_rdy;

wire addr_neq;
wire flag_neq;
wire empty_n;
wire  full_n;
wire ena_w;
wire ena_r;

reg  flag_w;
reg  flag_r;

assign in_rdy = full_n;

assign addr_neq = (addr_w != addr_r);
assign flag_neq = (flag_w != flag_r);
assign empty_n = flag_neq | addr_neq;
assign full_n = ~flag_neq | addr_neq;

assign ena_w =  full_n & in_vld;
assign ena_r = empty_n & read_rdy;

always @(posedge clk)
begin
    if (rst)
    begin
        flag_w <= 1'b0;
        flag_r <= 1'b0; 
    end
    else
    begin
        if (ena_w)
            flag_w <= flag_w ^ last_w;
        if (ena_r)
            flag_r <= flag_r ^ last_r;
    end
end

zq_counter #(
    .N ( DEPTH )
)
inst_wtptr (
    .clk                     ( clk     ),
    .rst                     ( rst     ),
    .clken                   ( ena_w   ),
    .last                    ( last_w  ),
    .out                     ( addr_w  )
);

zq_counter #(
    .N ( DEPTH )
)
inst_rdptr (
    .clk                     ( clk     ),
    .rst                     ( rst     ),
    .clken                   ( ena_r   ),
    .last                    ( last_r  ),
    .out                     ( addr_r  )
);

zq_sdpram_hs #(
    .ADDR_WIDTH ( ADDR_WIDTH  ),
    .DATA_WIDTH (      WIDTH  ),
    .DEPTH      (      DEPTH  ),
    .LATENCY    ( RAMLATENCY  ),
    .RAMTYPE    ( RAMTYPE     )
)
inst_sdpram_hs (
    .clk                     ( clk         ),
    .rst                     ( rst         ),

    .write_vld               ( ena_w       ),
    .addr_w                  ( addr_w      ),
    .din                     ( din         ),

    .read_rdy                ( read_rdy    ),
    .read_vld                ( ena_r       ),
    .addr_r                  ( addr_r      ),

    .out_rdy                 ( out_rdy     ),
    .out_vld                 ( out_vld     ),
    .dout                    ( dout        )
);

endmodule

